//! Expression AST nodes for NRC language

use super::ResolvedFunction;
use crate::error::Location;
use serde::{Deserialize, Serialize};

/// All possible expressions in NRC
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Expression {
    /// Literal values
    Literal(Literal),
    /// Variable reference
    Variable(String),
    /// Function call
    Call(CallExpr),
    /// Method call
    MethodCall(MethodCallExpr),
    /// Binary operation
    Binary(BinaryExpr),
    /// Unary operation
    Unary(UnaryExpr),
    /// Try propagation operation (expr?)
    Try(TryExpr),
    /// Force unwrap operation (expr!)
    ForceUnwrap(ForceUnwrapExpr),
    /// Conditional expression (ternary)
    Conditional(ConditionalExpr),
    /// Array/vector literal
    Array(ArrayExpr),
    /// Slice literal (Go-style: []T{...})
    Slice(SliceExpr),
    /// Map literal
    Map(MapExpr),
    /// Tuple literal
    Tuple(TupleExpr),
    /// Struct literal
    Struct(StructExpr),
    /// Field access
    FieldAccess(FieldAccessExpr),
    /// Index access
    IndexAccess(IndexAccessExpr),
    /// Slice range access (e.g., slice[1:3], slice[:5], slice[2:])
    SliceRange(SliceRangeExpr),
    /// Type cast
    Cast(CastExpr),
    /// Type assertion
    Assert(AssertExpr),
    /// Lambda/closure expression
    Lambda(LambdaExpr),
    /// Match expression
    Match(MatchExpr),
    /// Block expression
    Block(BlockExpr),
    /// If expression
    If(IfExpr),
    /// For expression
    For(ForExpr),
    /// While expression
    While(WhileExpr),
    /// Select expression
    Select(SelectExpr),
    /// Go expression (goroutine)
    Go(GoExpr),
    /// New expression for creating reference-counted objects
    New(NewExpr),
    /// Reference counted object construction: rc<T>{value}
    Rc(RcExpr),
    /// Path expression (e.g., Status::Pending, std::fmt::println)
    Path(PathExpr),
    /// Function literal/reference - represents a function as a value
    FunctionLiteral(FunctionLiteralExpr),
    /// Panic expression (Go-style: panic(value))
    Panic(PanicExpr),
}

/// Literal values
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Literal {
    /// Integer literal
    Integer(i64),
    /// Float literal
    Float(f64),
    /// String literal
    String(String),
    /// Character literal
    Char(char),
    /// Boolean literal
    Boolean(bool),
    /// Null literal
    Null,
}

/// Function call expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallExpr {
    /// Function name or expression
    pub callee: Box<Expression>,
    /// Type arguments
    pub type_args: Vec<Type>,
    /// Function arguments
    pub arguments: Vec<Expression>,
    /// Resolved function target (filled after overload resolution)
    pub resolved: Option<ResolvedFunction>,
    /// Location in source
    pub location: Location,
}

/// Method call expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MethodCallExpr {
    /// Object expression
    pub object: Box<Expression>,
    /// Method name
    pub method: String,
    /// Type arguments
    pub type_args: Vec<Type>,
    /// Method arguments
    pub arguments: Vec<Expression>,
    /// Location in source
    pub location: Location,
}

/// Binary operation expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BinaryExpr {
    /// Left operand
    pub left: Box<Expression>,
    /// Binary operator
    pub operator: BinaryOp,
    /// Right operand
    pub right: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Binary operators
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BinaryOp {
    // Arithmetic
    /// Addition operator (+)
    Add, // +
    /// Subtraction operator (-)
    Sub, // -
    /// Multiplication operator (*)
    Mul, // *
    /// Division operator (/)
    Div, // /
    /// Modulo operator (%)
    Mod, // %
    /// Power operator (**)
    Pow, // **

    // Bitwise
    /// Bitwise AND operator (&)
    BitAnd, // &
    /// Bitwise OR operator (|)
    BitOr, // |
    /// Bitwise XOR operator (^)
    BitXor, // ^
    /// Left bit shift operator (<<)
    LeftShift, // <<
    /// Right bit shift operator (>>)
    RightShift, // >>

    // Logical
    /// Logical AND operator (&&)
    LogicalAnd, // &&
    /// Logical OR operator (||)
    LogicalOr, // ||

    // Comparison
    /// Equality operator (==)
    Equal, // ==
    /// Inequality operator (!=)
    NotEqual, // !=
    /// Less than operator (<)
    Less, // <
    /// Less than or equal operator (<=)
    LessEqual, // <=
    /// Greater than operator (>)
    Greater, // >
    /// Greater than or equal operator (>=)
    GreaterEqual, // >=

    // Assignment
    /// Assignment operator (=)
    Assign, // =
    /// Add and assign operator (+=)
    AddAssign, // +=
    /// Subtract and assign operator (-=)
    SubAssign, // -=
    /// Multiply and assign operator (*=)
    MulAssign, // *=
    /// Divide and assign operator (/=)
    DivAssign, // /=
    /// Modulo and assign operator (%=)
    ModAssign, // %=
    /// Bitwise AND and assign operator (&=)
    BitAndAssign, // &=
    /// Bitwise OR and assign operator (|=)
    BitOrAssign, // |=
    /// Bitwise XOR and assign operator (^=)
    BitXorAssign, // ^=
    /// Left shift and assign operator (<<=)
    LeftShiftAssign, // <<=
    /// Right shift and assign operator (>>=)
    RightShiftAssign, // >>=
}

/// Unary operation expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnaryExpr {
    /// Unary operator
    pub operator: UnaryOp,
    /// Operand
    pub operand: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Unary operators
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum UnaryOp {
    /// Logical not (!)
    Not,
    /// Bitwise not (~)
    BitNot,
    /// Unary minus (-)
    Neg,
    /// Unary plus (+)
    Pos,
    /// Dereference (*)
    Deref,
    /// Address of (&)
    AddrOf,
}

/// Try expression (expr?)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TryExpr {
    /// Expression being unwrapped with propagation
    pub expression: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Force unwrap expression (expr!)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ForceUnwrapExpr {
    /// Expression being forcefully unwrapped
    pub expression: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Conditional expression (ternary operator)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConditionalExpr {
    /// Condition
    pub condition: Box<Expression>,
    /// True branch
    pub true_expr: Box<Expression>,
    /// False branch
    pub false_expr: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Array literal expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArrayExpr {
    /// Array elements
    pub elements: Vec<Expression>,
    /// Location in source
    pub location: Location,
}

/// Slice literal expression (Go-style: []T{elements...})
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SliceExpr {
    /// Element type
    pub element_type: Type,
    /// Slice elements
    pub elements: Vec<Expression>,
    /// Capacity parameter (optional)
    pub capacity: Option<Box<Expression>>,
    /// Count parameter for repeating elements (optional)
    pub count: Option<Box<Expression>>,
    /// Location in source
    pub location: Location,
}

/// Map literal expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MapExpr {
    /// Map entries
    pub entries: Vec<MapEntry>,
    /// Location in source
    pub location: Location,
}

/// Map entry
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MapEntry {
    /// Key
    pub key: Expression,
    /// Value
    pub value: Expression,
    /// Location in source
    pub location: Location,
}

/// Tuple literal expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TupleExpr {
    /// Tuple elements
    pub elements: Vec<Expression>,
    /// Location in source
    pub location: Location,
}

/// Struct literal expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StructExpr {
    /// Struct type
    pub struct_type: Type,
    /// Field initializers
    pub fields: Vec<FieldInit>,
    /// Location in source
    pub location: Location,
}

/// Field initializer
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldInit {
    /// Field name
    pub name: String,
    /// Field value
    pub value: Expression,
    /// Location in source
    pub location: Location,
}

/// Field access expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldAccessExpr {
    /// Object expression
    pub object: Box<Expression>,
    /// Field name
    pub field: String,
    /// Location in source
    pub location: Location,
}

/// Index access expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexAccessExpr {
    /// Object expression
    pub object: Box<Expression>,
    /// Index expression
    pub index: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Slice range expression (Go/Python-style slicing: slice[start:end])
/// Supports: [1:3], [:3], [2:], [:]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SliceRangeExpr {
    /// Object expression (the slice or array being sliced)
    pub object: Box<Expression>,
    /// Start index (optional, None means from beginning)
    pub start: Option<Box<Expression>>,
    /// End index (optional, None means to end)
    pub end: Option<Box<Expression>>,
    /// Location in source
    pub location: Location,
}

/// Type cast expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CastExpr {
    /// Expression to cast
    pub expr: Box<Expression>,
    /// Target type
    pub target_type: Type,
    /// Location in source
    pub location: Location,
}

/// Type assertion expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssertExpr {
    /// Expression to assert
    pub expr: Box<Expression>,
    /// Asserted type
    pub asserted_type: Type,
    /// Location in source
    pub location: Location,
}

/// Lambda/closure expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LambdaExpr {
    /// Lambda parameters
    pub parameters: Vec<Parameter>,
    /// Return type
    pub return_type: Option<Type>,
    /// Captured variables (filled during semantic analysis)
    pub captures: Vec<CapturedVar>,
    /// Lambda body
    pub body: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Captured variable information for lambda expressions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapturedVar {
    /// Original variable name
    pub name: String,
    /// Variable type
    pub var_type: Type,
    /// Capture mode (by value vs by reference)
    pub mode: CaptureMode,
}

/// Capture strategy for lambda variables
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum CaptureMode {
    /// Capture by value (current default)
    ByValue,
    /// Capture by reference (future expansion)
    ByReference,
}

/// Match expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchExpr {
    /// Expression to match
    pub expr: Box<Expression>,
    /// Match arms
    pub arms: Vec<MatchArm>,
    /// Location in source
    pub location: Location,
}

/// Match arm
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchArm {
    /// Pattern
    pub pattern: Pattern,
    /// Guard condition (if any)
    pub guard: Option<Expression>,
    /// Arm body
    pub body: Expression,
    /// Location in source
    pub location: Location,
}

/// Pattern for matching
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Pattern {
    /// Literal pattern
    Literal(Literal),
    /// Variable pattern
    Variable(String),
    /// Wildcard pattern
    Wildcard,
    /// Tuple pattern
    Tuple(Vec<Pattern>),
    /// Struct pattern
    Struct(StructPattern),
    /// Array pattern
    Array(Vec<Pattern>),
    /// Type pattern
    Type(Type),
    /// Or pattern (alternative patterns)
    Or(Vec<Pattern>),
    /// Enum pattern (e.g., Result::Ok(value))
    Enum(EnumPattern),
}

/// Enum pattern for matching enum variants
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnumPattern {
    /// Enum type name
    pub enum_name: String,
    /// Variant name
    pub variant_name: String,
    /// Patterns for variant data
    pub patterns: Vec<Pattern>,
    /// Location in source
    pub location: Location,
}

/// Struct pattern
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StructPattern {
    /// Struct type
    pub struct_type: Type,
    /// Field patterns
    pub fields: Vec<FieldPattern>,
    /// Location in source
    pub location: Location,
}

/// Field pattern
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldPattern {
    /// Field name
    pub name: String,
    /// Field pattern
    pub pattern: Pattern,
    /// Location in source
    pub location: Location,
}

/// Block expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockExpr {
    /// Statements in the block
    pub statements: Vec<Statement>,
    /// Final expression (if any)
    pub final_expr: Option<Box<Expression>>,
    /// Location in source
    pub location: Location,
}

/// If expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IfExpr {
    /// Condition
    pub condition: Box<Expression>,
    /// Then branch
    pub then_branch: Box<Expression>,
    /// Else branch (if any)
    pub else_branch: Option<Box<Expression>>,
    /// Location in source
    pub location: Location,
}

/// For expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ForExpr {
    /// Loop variable
    pub variable: String,
    /// Iterable expression
    pub iterable: Box<Expression>,
    /// Loop body
    pub body: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// While expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WhileExpr {
    /// Condition
    pub condition: Box<Expression>,
    /// Loop body
    pub body: Box<Expression>,
    /// Location in source
    pub location: Location,
}

/// Select expression
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelectExpr {
    /// Select cases
    pub cases: Vec<SelectCase>,
    /// Default case (if any)
    pub default_case: Option<Box<Expression>>,
    /// Location in source
    pub location: Location,
}

/// Select case
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelectCase {
    /// Channel expression
    pub channel: Expression,
    /// Case body
    pub body: Expression,
    /// Location in source
    pub location: Location,
}

/// Go expression (goroutine)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GoExpr {
    /// Function call to execute in goroutine
    pub call: CallExpr,
    /// Location in source
    pub location: Location,
}

/// New expression for creating reference-counted objects
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewExpr {
    /// Type to instantiate
    pub type_name: String,
    /// Initializer expression (struct literal, etc.)
    pub initializer: Option<Box<Expression>>,
    /// Location in source
    pub location: Location,
}

/// Reference counted object construction: rc<T>{value}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RcExpr {
    /// Inner type being reference counted
    pub inner_type: Box<Type>,
    /// Initializer expression (literal, struct, etc.)
    pub initializer: Option<Box<Expression>>,
    /// Location in source
    pub location: Location,
}

/// Path expression for accessing enum variants and module items (e.g., Status::Pending, std::fmt::println)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathExpr {
    /// Path segments
    pub segments: Vec<String>,
    /// Location in source
    pub location: Location,
}

/// Function literal expression - represents a function as a first-class value
/// This can reference either a named function or a lambda/closure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionLiteralExpr {
    /// Function name (for named function references)
    pub function_name: Option<String>,
    /// Lambda expression (for anonymous functions)
    pub lambda: Option<Box<LambdaExpr>>,
    /// Function type (inferred or explicitly specified)
    pub function_type: Option<Type>,
    /// Location in source
    pub location: Location,
}

/// Panic expression (Go-style: panic(value))
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PanicExpr {
    /// Value to panic with
    pub value: Box<Expression>,
    /// Location in source
    pub location: Location,
}

// Re-export types from types module
use crate::ast::stmt::Statement;
use crate::ast::types::{Parameter, Type};

impl Expression {
    /// Get the location of this expression
    pub fn location(&self) -> Location {
        match self {
            Expression::Literal(lit) => match lit {
                Literal::Integer(_)
                | Literal::Float(_)
                | Literal::String(_)
                | Literal::Char(_)
                | Literal::Boolean(_)
                | Literal::Null => {
                    Location::new(0, 0, 0) // Literals don't have location info
                }
            },
            Expression::Variable(_) => Location::new(0, 0, 0),
            Expression::Call(expr) => expr.location,
            Expression::MethodCall(expr) => expr.location,
            Expression::Binary(expr) => expr.location,
            Expression::Unary(expr) => expr.location,
            Expression::Try(expr) => expr.location,
            Expression::ForceUnwrap(expr) => expr.location,
            Expression::Conditional(expr) => expr.location,
            Expression::Array(expr) => expr.location,
            Expression::Slice(expr) => expr.location,
            Expression::Map(expr) => expr.location,
            Expression::Tuple(expr) => expr.location,
            Expression::Struct(expr) => expr.location,
            Expression::FieldAccess(expr) => expr.location,
            Expression::IndexAccess(expr) => expr.location,
            Expression::SliceRange(expr) => expr.location,
            Expression::Cast(expr) => expr.location,
            Expression::Assert(expr) => expr.location,
            Expression::Lambda(expr) => expr.location,
            Expression::Match(expr) => expr.location,
            Expression::Block(expr) => expr.location,
            Expression::If(expr) => expr.location,
            Expression::For(expr) => expr.location,
            Expression::While(expr) => expr.location,
            Expression::Select(expr) => expr.location,
            Expression::Go(expr) => expr.location,
            Expression::New(expr) => expr.location,
            Expression::Rc(expr) => expr.location,
            Expression::Path(expr) => expr.location,
            Expression::FunctionLiteral(expr) => expr.location,
            Expression::Panic(expr) => expr.location,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_literal_expressions() {
        let int_lit = Expression::Literal(Literal::Integer(42));
        let str_lit = Expression::Literal(Literal::String("hello".to_string()));
        let bool_lit = Expression::Literal(Literal::Boolean(true));

        assert!(matches!(int_lit, Expression::Literal(Literal::Integer(42))));
        assert!(matches!(str_lit, Expression::Literal(Literal::String(_))));
        assert!(matches!(
            bool_lit,
            Expression::Literal(Literal::Boolean(true))
        ));
    }

    #[test]
    fn test_binary_expression() {
        let left = Expression::Literal(Literal::Integer(10));
        let right = Expression::Literal(Literal::Integer(20));
        let location = Location::new(1, 10, 10);

        let binary = Expression::Binary(BinaryExpr {
            left: Box::new(left),
            operator: BinaryOp::Add,
            right: Box::new(right),
            location,
        });

        assert!(matches!(binary, Expression::Binary(_)));
    }

    #[test]
    fn test_function_call() {
        let callee = Expression::Variable("add".to_string());
        let args = vec![
            Expression::Literal(Literal::Integer(1)),
            Expression::Literal(Literal::Integer(2)),
        ];
        let location = Location::new(1, 1, 0);

        let call = Expression::Call(CallExpr {
            callee: Box::new(callee),
            type_args: vec![],
            arguments: args,
            resolved: None,
            location,
        });

        assert!(matches!(call, Expression::Call(_)));
    }

    #[test]
    fn test_expression_location_literal() {
        let expr = Expression::Literal(Literal::Integer(42));
        let loc = expr.location();
        assert_eq!(loc.line, 0);
        assert_eq!(loc.column, 0);
    }

    #[test]
    fn test_expression_location_variable() {
        let expr = Expression::Variable("x".to_string());
        let loc = expr.location();
        assert_eq!(loc.line, 0);
        assert_eq!(loc.column, 0);
    }

    #[test]
    fn test_expression_location_call() {
        let location = Location::new(5, 10, 50);
        let call = Expression::Call(CallExpr {
            callee: Box::new(Expression::Variable("func".to_string())),
            type_args: vec![],
            arguments: vec![],
            resolved: None,
            location,
        });
        let loc = call.location();
        assert_eq!(loc.line, 5);
        assert_eq!(loc.column, 10);
    }

    #[test]
    fn test_expression_location_binary() {
        let location = Location::new(3, 15, 30);
        let binary = Expression::Binary(BinaryExpr {
            left: Box::new(Expression::Literal(Literal::Integer(1))),
            operator: BinaryOp::Add,
            right: Box::new(Expression::Literal(Literal::Integer(2))),
            location,
        });
        let loc = binary.location();
        assert_eq!(loc.line, 3);
        assert_eq!(loc.column, 15);
    }

    #[test]
    fn test_expression_location_unary() {
        let location = Location::new(2, 8, 20);
        let unary = Expression::Unary(UnaryExpr {
            operator: UnaryOp::Not,
            operand: Box::new(Expression::Literal(Literal::Boolean(true))),
            location,
        });
        let loc = unary.location();
        assert_eq!(loc.line, 2);
        assert_eq!(loc.column, 8);
    }

    #[test]
    fn test_expression_location_method_call() {
        let location = Location::new(7, 3, 70);
        let method_call = Expression::MethodCall(MethodCallExpr {
            object: Box::new(Expression::Variable("obj".to_string())),
            method: "method".to_string(),
            type_args: vec![],
            arguments: vec![],
            location,
        });
        let loc = method_call.location();
        assert_eq!(loc.line, 7);
        assert_eq!(loc.column, 3);
    }

    #[test]
    fn test_all_literal_types() {
        let int_lit = Expression::Literal(Literal::Integer(42));
        let float_lit = Expression::Literal(Literal::Float(3.14));
        let str_lit = Expression::Literal(Literal::String("hello".to_string()));
        let char_lit = Expression::Literal(Literal::Char('a'));
        let bool_lit = Expression::Literal(Literal::Boolean(true));
        let null_lit = Expression::Literal(Literal::Null);

        assert!(matches!(int_lit, Expression::Literal(Literal::Integer(_))));
        assert!(matches!(float_lit, Expression::Literal(Literal::Float(_))));
        assert!(matches!(str_lit, Expression::Literal(Literal::String(_))));
        assert!(matches!(char_lit, Expression::Literal(Literal::Char(_))));
        assert!(matches!(bool_lit, Expression::Literal(Literal::Boolean(_))));
        assert!(matches!(null_lit, Expression::Literal(Literal::Null)));
    }
}
